import 'log_entity.dart';
import 'log_printer.dart';
import 'ansi_color.dart';
import 'dart:convert';




/// Default implementation of [LogPrinter].
///
/// Outut looks like this:
/// ```
/// ┌──────────────────────────
/// │ Error info
/// ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
/// │ Method stack history
/// ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
/// │ Log message
/// └──────────────────────────
/// ```
class PrettyPrinter extends LogPrinter {
  static const String  topLeftCorner = '┌';
  static const String bomttomLeftCorner = '└';
  static const String middleCorner = '├';
  static const String singleDivider = '┄';
  static const String verticalLine = '│';
  static const String doubleDivider = '─';

  static const String DOUBLE_DIVIDER = '────────────────────────────────────────────────────────';
  static const String SINGLE_DIVIDER = '┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄';

  static final levelColors = {
    Level.debug: AnsiColor.fg(38),
    Level.error: AnsiColor.fg(1),
    Level.info: AnsiColor.fg(38),
  };

  /*
    堆栈信息正则
    
   * 匹配设备堆栈信息
   *
   * 匹配规则：
   *  #0      _MyAppState.build (package:my_app/main.dart:33:12)
   *  #1      StatefulElement.build (package:flutter/src/widgets/framework.dart:4619:28)
   *  #2      ComponentElement.performRebuild ()
   */
  static final _deviceStackTraceRegex = RegExp(r'#[0-9]+[\s]+(.+) \(([^\s]+)\)');

  /*
    第一个捕获组 ((packages|dart-sdk)\/[^\s]+\/): 匹配到的整个路径前缀。
    例如：packages/my_app/, packages/flutter/, dart-sdk/lib/
    第二个捕获组 (packages|dart-sdk): 匹配到的是 packages 还是 dart-sdk。
    例如：packages, dart-sdk
   */
  static final _webStackTraceRegex = RegExp(r'^((packages|dart-sdk)\/[^\s]+\/)');

  static DateTime? _startTime;

  final int methodCount;
  final int errorMethodCount;
  final int lineLength;
  final bool colors;
  final bool printTime;

  String _topBorder = '';
  String _bottomBorder = '';
  String _middleBorder = '';

  PrettyPrinter({
    this.methodCount = 0,
    this.errorMethodCount = 8,
    this.lineLength = 100,
    this.colors = true,
    this.printTime = true,
  }){
    /// 初始化开始时间
    /// ??= 表示如果 _startTime 为 null，则赋值给它 DateTime.now()
    _startTime ??= DateTime.now();

    /// 初始化边框
    /// 创建一个字符串，将四个字符 '─' 连接起来，并加上一个换行符。
    _topBorder = '$topLeftCorner$DOUBLE_DIVIDER';
    _bottomBorder = '$bomttomLeftCorner$DOUBLE_DIVIDER';
    _middleBorder = '$middleCorner$SINGLE_DIVIDER';
  }




  /// 重写 log 方法
  @override
  List<String> log(LogEvent event) {
    var messageStr = stringfyMessage(event.message);

    /// 处理异常信息
    String? stackTraceStr;
    if(event.stackTrace != null){
      if(methodCount > 0){
        stackTraceStr = formatStackTrace(event.stackTrace!, methodCount);
      }
    }else if(errorMethodCount > 0){
      stackTraceStr = formatStackTrace(StackTrace.current, errorMethodCount);
    }

    var errorStr = event.error?.toString();

    String timeStr = '';
    if(printTime){
      timeStr = getTime();
    }

    return _formatAndPrint(
        event.level,
        messageStr,
        timeStr,
        errorStr ?? '',
        stackTraceStr ?? '',
    );
  }


  String stringfyMessage(dynamic message) { 
    if(message is Map || message is List){
      /// 知识点：JsonEncoder.withIndent
      /// 这个方法创建了一个 JSON 编码器，并且指定了缩进字符串
      /// 缩进字符串是两个空格
      /// 这样生成的 JSON 字符串会更易读
      return const JsonEncoder.withIndent('  ').convert(message);
    } else {
      return message.toString();
    }
  }

  /// 格式化堆栈信息 todo 明天来看
  /// 返回String格式 参数：
  /// 知识点：
  /// 1. StackTrace 的处理：StackTrace 是 Dart 中用于捕获异常时的调用堆栈信息。
  /// 通过 formatStackTrace 方法可以将其格式化为更易读的字符串。
  /// 
  String? formatStackTrace(StackTrace stackTrace, int methodCount) {
    var lines = stackTrace.toString().split('\n');
    var formatted = <String>[];
    var count = 0;

    for (var line in lines) {
      if(_discardDeviceStackTrace(line)||_discardWebStackTrace(line)){
        continue;
      }
      /// r'#\d+\s' 是一个正则表达式，匹配以 # 开头，后跟一个或多个数字，然后是一个空格的字符串
      /// 例如：#0 、#12 、#345 等等
      /// 替换掉匹配到的字符串为空字符串
      /// 添加到新的列表中
      formatted.add('#$count    ${line.replaceFirst(RegExp(r'#\d+\s'), '')}');
      if (++count == methodCount) {
        break;
      }
    }

    if (formatted.isEmpty) {
      return null;
    }
    /// 将列表中的元素连接成一个字符串并返回
    /// 每个元素之间用换行符分隔
    return formatted.join('\n');
  }

  /// 判断是否丢弃 device 堆栈信息
  /// 匹配堆栈信息中包含 'package:logger' 的行
  /// 如果匹配到则返回 true，表示该行是 logger 包的堆栈信息，应当丢弃
  /// 否则返回 false，表示该行不是 logger 包的堆栈信息，应当保留
  /// 参数 line 是堆栈信息中的一行字符串
  bool _discardDeviceStackTrace(String line){
    /// firstMatch 方法返回第一个匹配项，如果没有匹配项，则返回 null
    var match = _deviceStackTraceRegex.firstMatch(line);
    if(match == null) return false;
    /// match.group(2) 是匹配项的第二个分组，即匹配到的字符串
    /// 为什么匹配第二个? 因为第一个分组是匹配到的行号，第二个分组才是匹配到的字符串
    /// startsWith 方法用于判断字符串是否以指定的子字符串开头
    /// 如果是，则返回 true；否则返回 false
    /// 所以这行代码的意思是：
    /// 如果匹配到的字符串以 'package:logger' 开头，则返回 true；否则返回 false
    return match.group(2)?.startsWith('package:logger')??false;
  }

  bool _discardWebStackTrace(String line){
    var match = _webStackTraceRegex.firstMatch(line);
    if(match == null) return false;
    return (match.group(2)?.startsWith('package/logger')??false)||
      (match.group(2)?.startsWith('dart-sdk/lib')??false);
  }



  String getTime(){
    /// 计算从应用启动到当前的时间差
    /// _threeDigits 方法用于将数字 n 转换为 3 位字符串。
    /// 如果 n 小于 10，则在前面补两个零；
    /// 如果 n 小于 100，则在前面补一个零；
    /// 否则直接返回 n 的字符串形式。
    String _threeDigits(int n) {
      if (n >= 100) return "$n";
      if (n >= 10) return "0$n";
      return "00$n";
    }

    String _twoDigits(int n) {
      if (n >= 10) return "$n";
      return "0$n";
    }

    var now = DateTime.now();
    var y = now.year;
    var m = _twoDigits(now.month);
    var d = _twoDigits(now.day);
    var h = _twoDigits(now.hour);
    var min = _twoDigits(now.minute);
    var sec = _twoDigits(now.second);
    var ms = _threeDigits(now.millisecond);

    /// timeSinceStart表示从程序启动到当前时间间隔
    /// timeSinceStart 的格式为：HH:mm:ss.ms
    /// _startTime 是一个 DateTime 对象，表示程序启动的时间。
    var timeSinceStart = now.difference(_startTime ?? DateTime.now()).toString();
    return "$y/$m/$d  $h:$min:$sec.$ms  (+$timeSinceStart)";
  }


  AnsiColor _getLevelColor(Level level){
    
    print(  "这个level${level}");
    // return colors ? levelColors[level] ?? AnsiColor.none() : AnsiColor.none();
    if (colors) {
      return levelColors[level]!;
    } else {
      return AnsiColor.none();
    }
  }

  AnsiColor _getErrorColor(Level level){
    // return colors ? levelColors[level] ?? AnsiColor.none() : AnsiColor.none();
    if (colors) {
      return levelColors[level]!;
    } else {
      return AnsiColor.none();
    }
  }

/*
  List<String> _formatAndPrint(
    Level level,
    String message,
    String time,
    String error,
    String stackTrace,
  ){
    List<String> buffer = [];
    /// _getLevelColor 返回的是 AnsiColor 对象
    /// 该对象重写了 call 方法，可以像函数一样调用，将字符串 msg 添加到 buffer 中。
    var color = _getLevelColor(level);

    /// 为什么可以用 color(msg)？
    /// 因为 AnsiColor 类重写了 call 方法，使得 AnsiColor 的实例可以像函数一样被调用
    /// 这个 call 方法接受一个字符串参数 msg，并返回一个带有 ANSI 颜色码的字符串
    /// 如果 colors 为 true，则返回的字符串会被包裹在相应的颜色码中，从而在终端显示为彩色
    /// 如果 colors 为 false，则直接返回原始的 msg 字符串，不添加任何颜色码
    buffer.add(color(_topBorder));


    if(error != ''){
      var errorColor = _getErrorColor(level);
      for (var line in error.split('\n')){
        buffer.add(
          color(verticalLine) +
          errorColor.resetForeground +
          errorColor(line) +
          errorColor.resetBackground,
        );
      }
      buffer.add(color(_middleBorder));
    }

    if(stackTrace !=  ''){
      for(var line in stackTrace.split('\n')){
        buffer.add(color('$color$verticalLine  $line'));
      }
      buffer.add(color(_middleBorder));
    }


    if(time !=  ''){
      buffer
      /// .. 级联操作符，表示对同一个对象进行多次操作
      /// 这里表示对 buffer 对象进行多次 add 操作
      ..add(color('$verticalLine  $time'))
      ..add(color(_middleBorder));
    }

    for (var line in message.split('\n')) {
      buffer.add(color('$verticalLine  $line'));
    }

    buffer.add(color(_bottomBorder));

    return buffer;
  }
  */

  List<String> _formatAndPrint(
    Level level,
    String message,
    String time,
    String error,
    String stackTrace,
  ){
    final buffer = <String>[];
    // final color = _getLevelColor(level);
    buffer.add('\n--------------开始-----------------');
    buffer.add('\n');
    buffer.add((time));
    buffer.add('\n');
    for(var line in stackTrace.split('\n')){
      buffer.add('\n');
      buffer.add((line));
    }
    buffer.add('\n');
    for (var line in message.split('\n')) {
      buffer.add('\n');
      buffer.add((line));
    }
    buffer.add('\n');
    buffer.add('--------------结束-----------------\n');
    return buffer;
  }

}